﻿import { getLocal, addLocal, hashCode, calcSimilarity, MessageRouter } from '../util'
import { memoize, forEach } from 'lodash'

const reWhite               = /jsp|HTML|angular|sql|gulp|grunt|webpack|css|乱码|validate|View|engine|Page|码云|webview|织梦|后台|上传|调用|设置|配置|函数|echart|安装|调试|部署|同步|的问题|Java|Android|PHP|MySQL|JFinal|Spring|Python|Eclipse|jQuery|Tomcat|Linux|ECharts|Ubuntu|Nginx|CentOS|Oracle|Android SDK|Hibernate|iOS|Apache|Struts|Maven|MyEclipse|Git|Qt|IntelliJ IDEA|Hadoop|C\+\+|Windows|ExtJS|MongoDB|Java EE|Xcode|Visual Studio|EasyUI|MyBatis|Druid|Lucene|Django|Redis|SQL Server|Subversion|SVN|jQuery Mobile|JSON|PostgreSQL|SpringSide|Fedora Linux|Chrome|zTree|Netty|JavaScript|JDK|KindEditor|FreeMarker|ThinkPHP|Shiro|memcached|Bootstrap|Node|websocket|ehcache|ActiveMQ|rocketmq|Build|search|bean|factory|initial|application|context/i, // 白名单
    baseUrl                  = 1 === 1 ? 'https://www.oschina.net' : 'http://www.oschina.org',
    iconUrl                  = chrome.runtime.getURL('img/osc-link-48.png'),
    domParser                = new DOMParser(),
    similarityThreshold      = 0.15, // 单条检测重复度阈值, 大于等于才加入总重复度, 范围0-1.
    totalSimilarityThreshold = 1, // 总重复度阈值, 和其他帖子重复度之和大于等于此数才会列为可疑帖子.
    checkInterval            = 60e3, // 检测间隔, 60秒
    messageRouter            = new MessageRouter()

let isAdmin = false, // 是否管理员
    userCode = null,
    processed = getLocal('processed', []), // 已处理过的帖子id
    ignored = getLocal('ignored', []), // 已忽略的帖子id
    lastProcessed = getLocal('lastProcessed', ["2016年统计师考试| 答案★25０525822★","2016年10月自考考试题|答案★25０525822★","2016年成人高考考试题|答案★25０525822★","2016年注册会计师考试丨答案 250525822","2016年10月自考考试丨答案 25052 5822","2016年审计师考试题|答案★332７７83７★","2016年成考考试题|答案★59０4０981★","2016年成人高考考试题|答案★59０4０981★","2016年10月自学考试| 答案★33 2７７ 83７★","2016年经济师考试题 |答案★33 2７７ 83７★"]), // 过去十条处理过的帖子标题, 用来捉一些漏网之鱼
    pending = new Proxy({}, { // 待处理的帖子
      set(obj, prop, val) {
        obj[prop] = val
        updateState()
        return val
      },
      deleteProperty(obj, prop) {
        let res = delete obj[prop]
        closeNotification(prop)
        updateState()
        return res
      }
    })

let getArticleHashCode = memoize(href =>
  hashCode(href.substr(href.lastIndexOf('/') + 1).split('_').reverse().join('_')))

// 处理帖子
function process(article) {
  // 是管理员则删除, 否则举报
  fetch(isAdmin ? `${baseUrl}/action/question/delete?id=${article.id}&hash=${article.hash}&user_code=${userCode}`
      : `${baseUrl}/action/communityManage/report?obj_id=${article.id}&obj_type=2&url=${encodeURIComponent(article.href)}&user_code=${userCode}&reason=1&memo=`, { credentials: 'include', method: 'POST' })
    .then(res => res.text())
    .then(res => {
      if (res.length) {
        chrome.notifications.create(article.id.toString(), {
          type: 'basic',
          title: `帖子"${article.title}"操作失败, 点击前往该贴`,
          message: res,
          iconUrl
        })
      } else {
        processed = addLocal('processed', article.id)
        delete pending[article.id]
      }
    })
  lastProcessed = addLocal('lastProcessed', article.title, 10)

  closeNotification(article.id)
}

function processAll() {
  forEach(pending, process)
}

function ignore(article) {
  ignored = addLocal('ignored', article.id)
  delete pending[article.id]
  closeNotification(article.id)
}

function closeNotification(articleId) {
  chrome.notifications.clear(articleId.toString())
}

function checkAD(doc) {
  let pendingToRemove = new Set(Object.keys(pending))
  let 疑似广告贴 = Array.from(doc.querySelectorAll('#HomeTopicPanel a[title]:not(.top), .list li:not(.top) a[target]')) // 不检查置顶贴
    .map(a => ({
      id: a.href.substr(a.href.indexOf('_') + 1),
      hash: getArticleHashCode(a.href),
      href: a.href,
      title: a.title || a.innerText,
      el: a,
      similaritySum: 0
    }))

    .filter(article => !reWhite.test(article.title) // 白名单过滤
      && !ignored.includes(article.id)
      && !processed.includes(article.id)
      && (pendingToRemove.delete(article.id), !pending[article.id])
    )

    .map((article, idx, arr) => { // 计算和其他帖子的重复度
      for (let i = idx + 1, len = arr.length; i < len; ++i) {
        let similarity = calcSimilarity(article.title, arr[i].title)
        if (similarity >= similarityThreshold) { // 大于等于单条检测重复度阈值
          console.debug(similarity, article.title, arr[i].title)
          article.similaritySum += similarity
          arr[i].similaritySum += similarity
        }
      }
      lastProcessed.forEach(title => { // 计算和过去删除的十条帖子重复度
        let similarity = calcSimilarity(article.title, title)
        if (similarity >= similarityThreshold) { // 大于等于单条检测重复度阈值
          console.debug(similarity, article.title, 'last processed: ' + title)
          article.similaritySum += similarity
        }
      })

      return article
    })

    .filter(article => article.similaritySum >= totalSimilarityThreshold) // 根据总重复度阈值过滤

  // 删除已经不在首页的待处理帖子(可以认为已经被处理)
  if (pendingToRemove.size) {
    for (let id of pendingToRemove) {
      delete pending[id]
    }
  }

  if (疑似广告贴.length) {
    console.info(疑似广告贴)
    疑似广告贴.forEach(article => {
      pending[article.id] = article
    })
  }

  提示广告贴()
}

function updateState() {
  chrome.browserAction.setBadgeText({
    text: (Object.keys(pending).length || '🐸').toString()
  })
  chrome.browserAction.setTitle({
    title: `工作中, 累计已处理${processed.length}帖子`
  })

  chrome.runtime.sendMessage({
    route: 'popup/pending',
    data: pending
  })
}

function checkLogin() {
  return new Promise(resolve => {
    fetchLoginState(resolve)
  })
}
function fetchLoginState(succCb) {
  fetch(baseUrl, { credentials: 'include' })
    .then(res => res.text())
    .then(res => {
      let doc = domParser.parseFromString(res, 'text/html'),
          userCodeInput = doc.querySelector('[name=user_code]')

      if (!userCodeInput) {
        chrome.notifications.create('not_logged_in', {
          type: 'basic',
          title: '未登录OSC',
          message: '请登录~',
          iconUrl
        })
        setTimeout(() => {
          fetchLoginState(succCb)
        }, checkInterval)
      } else {
        userCode = userCodeInput.value

        succCb()
      }
    })
}

function handleNotification() {
  chrome.notifications.onClicked.addListener(articleId => {
    let article = pending[articleId]
    if (article) {
      window.open(article.href)
    }
  })
  chrome.notifications.onButtonClicked.addListener((articleId, btnIdx) => {
    let article = pending[articleId]
    if (article) {
      switch (btnIdx) {
        case 0:
          process(article)
          break
        case 1:
          ignore(article)
          break
        default:
          break
      }
    } else {
      closeNotification(articleId)
    }
  })
}

function handleMessage() {
  messageRouter.register({
    background: {
      stat(data, msg, sender, sendResp) {
        sendResp({
          isAdmin: isAdmin,
          pending: pending
        })
      },
      process(id) {
        process(pending[id])
      },
      ignore(id) {
        ignore(pending[id])
      }
    }
  })
}

function handleCommand() {
  chrome.commands.onCommand.addListener(processAll)
}

function intervalCheck() {
  fetch(`${baseUrl}/question?catalog=1&show=time`, { credentials: 'include' })
    .then(res => res.text())
    .then(res => {
      checkAD(domParser.parseFromString(res, 'text/html'))
    })
    .catch(e => 0)

  setTimeout(intervalCheck, checkInterval)
}

function 提示广告贴() {
  forEach(pending, article => {
    chrome.notifications.create(article.id.toString(), {
      type: 'basic',
      title: '疑似广告贴',
      message: article.title,
      iconUrl,
      buttons: [{
        title: isAdmin ? '删除' : '举报'
      }, {
        title: '忽略'
      }]
    })
  })
}

export default () => {
  return checkLogin()
    .then(res => {
      return fetch(`${baseUrl}/action/question/delete?user_code=${userCode}`, {
        method: 'POST',
        credentials: 'include'
      })
    })
    .then(res => res.text())
    .then(res => {
      isAdmin = res.indexOf('权限不足') < 0

      handleNotification()

      handleMessage()

      handleCommand()

      intervalCheck()

      updateState()
    })
}